﻿
using System;
using System.Configuration.Provider;
using System.Net;
using System.Security.Permissions;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Description;
using Security.Authorizations.AuthorizationServices;
using System.Linq;
using System.Collections.Generic;

namespace Security.Authorizations
{
    public class ClientAuthorizationServiceProvider : AuthorizationProvider
    {
        public override string ApplicationName { get; set; }
     
        public override int CacheTimeout{ get; set; }
        
        public string ServiceUri { get; set; }

        public string UserName { get; set; }

        public string Password { get; set; }
      
        public string Domain { get; set; }

        public string Protocol { get; set; }

        public string Identity { get; set; }

         

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection attributes)
        {
            base.Initialize(name, attributes);

            if (string.IsNullOrEmpty(attributes["serviceUri"]))
            {
                throw new ProviderException("the parameter serviceUri is not set");
            }

            this.ServiceUri = attributes["serviceUri"];
            this.UserName = attributes["username"];
            this.Password = attributes["password"];
            this.Domain = attributes["domain"];
            this.Protocol = attributes["protocol"];
            this.Identity = attributes["identity"];
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            return IsUserInRole(username, roleName, null);
        }

        public override string[] GetRolesForUser(string username)
        {
            return GetRolesForUser(username, null);
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
             AddUsersToRoles(usernames, roleNames, null);
        }

        public override void CreateRole(string roleName)
        {
            CreateRole(roleName, roleName, null, null);
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            return DeleteRole(roleName, throwOnPopulatedRole, null);
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            return FindUsersInRole(roleName, usernameToMatch, null);
        }

        public override string[] GetAllRoles()
        {
            return GetAllRoles(null);
        }

        public override string[] GetUsersInRole(string roleName)
        {
            return GetUsersInRole(roleName, null);
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
             RemoveUsersFromRoles(usernames, roleNames, null);
        }

        public override bool RoleExists(string roleName)
        {
            return RoleExists(roleName, null);
        }

        public override string[] GetRolesForUser(string username, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution( i => i.GetRoleAssignmentsNamesForUser(username, this.ApplicationName, scopeName));
        }
      

        public override bool IsUserInRole(string username, string roleName, string scopeName)
        {
            var client = GetAuthorizationServiceClient();            
            return client.SafeExecution( i => i.IsUserInRoleAssignment(username, this.ApplicationName, scopeName, roleName));
                    
        }

        public override string[] GetOperationsForUser(string username)
        {
            return GetOperationsForUser(username, null);
        }

        public override string[] GetOperationsForUser(string username, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            var result = client.SafeExecution(i => i.GetOperationsForUser(username, this.ApplicationName, null)).Select(op => op.Name);
            return result.ToArray();
        }

        public override bool IsUserInTask(string username, string taskName)
        {
            return IsUserInTask(username, taskName, null);
        }

        public override bool IsUserInTask(string username, string taskName, string[] scopeNames)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(p => p.IsUserInTask(username, this.ApplicationName, taskName, scopeNames));
        }

        public override string[] GetTasksForUser(string username)
        {
            return GetTasksForUser(username, null);
        }

        public override bool AccessCheck(string username, string operationName)
        {
            return AccessCheck(username, operationName, null);
        }


        public override bool AccessCheck(string username, string operationName, string[] scopeNames)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(p => p.BulkAccessCheckByName(username, this.ApplicationName, new [] {operationName}, scopeNames)).Length > 0;
        }

        public override string[] AccessCheckAnyScope(string username, string[] operationNames, string scopeFilter)
        {
            var client = GetAuthorizationServiceClient();
            var operations = client.SafeExecution(p => p.BulkAccessCheckByNameInAnyScope(username, this.ApplicationName, scopeFilter, operationNames));
            List<string> result = new List<string>();
            foreach (var item in operations)
        	{
		        result.Add(item.Name);
	        }

            return result.ToArray();
        }

        public override string[] AccessCheck(string username, string[] operationNames, string scopeName)
        {
            string[] scopeNames = null;

            if (!string.IsNullOrEmpty(scopeName))
            {
                scopeNames = new[] { scopeName };
            }

            return AccessCheck(username, operationNames, scopeNames);
        }

        public override string[] AccessCheck(string username, string[] operationNames, string[] scopeNames)
        {
            var client = GetAuthorizationServiceClient();
            var operations = client.SafeExecution(p => p.BulkAccessCheckByName(username, this.ApplicationName, operationNames, scopeNames));
            List<string> resultNames = new List<string>();
            foreach ( var item in operations)
            {
                resultNames.Add(item.Name);
            }

            return resultNames.ToArray();
        }

        public override string[] GetTasksForUser(string username, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.GetTaskNamesForUser(username, this.ApplicationName, scopeName));
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            client.SafeExecution(i => i.AddUsersToRoleAssignments(this.ApplicationName, scopeName, usernames, roleNames));
        }

        public override void CreateRole(string roleName, string roleAssignmentName, string description, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            client.SafeExecution(i => i.CreateRoleAssignment(this.ApplicationName, scopeName, roleName, roleAssignmentName, description));
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole, string scopeName)
        {
            try
            {
                var client = GetAuthorizationServiceClient();
                client.SafeExecution(i => i.DeleteRoleAssignment(this.ApplicationName, scopeName, roleName));
                return true;
            }
            catch
            {
                return false;
            }
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.FindUsersInRoleAssignment(usernameToMatch, this.ApplicationName, scopeName, roleName));
        }

        public override string[] GetUsersInRole(string roleName, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.GetUsersInRoleAssignment(this.ApplicationName, scopeName, roleName));
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            client.SafeExecution(i => i.RemoveUsersFromRoleAssignments(this.ApplicationName, scopeName, usernames, roleNames));
        }

        public override bool RoleExists(string roleName, string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.RoleAssignmentExists(this.ApplicationName, scopeName, roleName));
        }

        public override string[] GetAllRoles(string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.GetAllRoleAssignmentNames(this.ApplicationName, scopeName));
        }
        
        public override string[] GetAllOperations()
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution( i => i.GetAllOperationNames(this.ApplicationName));
        }

        public override string[] GetAllTasks()
        {
            return GetAllTasks(null);
        }

        public override string[] GetAllTasks(string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.GetAllTaskNames(this.ApplicationName, scopeName));
        }

        private AuthorizationServiceClient GetAuthorizationServiceClient()
        {
            if (String.IsNullOrEmpty(Protocol))
                return GetJsonAuthorizationServiceClient();

            if (Protocol.Equals("http",StringComparison.InvariantCultureIgnoreCase))
                return GetJsonAuthorizationServiceClient();
            else if (Protocol.Equals("net.tcp", StringComparison.InvariantCultureIgnoreCase))
                return GetNetTcpAuthorizationServiceClient();

            throw new ProviderException("The protocol must be http or net.tcp");
        }

        private AuthorizationServiceClient GetNetTcpAuthorizationServiceClient()
        {

            //var client = new AuthorizationServiceClient(GetNetTcpBinding(), new EndpointAddress(new Uri(ServiceUri), EndpointIdentity.CreateUpnIdentity(string.Format("{0}\\{1}", Domain, UserName)), new System.ServiceModel.Channels.AddressHeader[] { }));

            var client = new AuthorizationServiceClient(GetNetTcpBinding(), new EndpointAddress(new Uri(ServiceUri), EndpointIdentity.CreateUpnIdentity(Identity), new System.ServiceModel.Channels.AddressHeader[] { }));
            client.ClientCredentials.Windows.ClientCredential = new NetworkCredential(UserName, Password, Domain);
            client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
            return client;
        }

        private static NetTcpBinding GetNetTcpBinding()
        {
            var binding = new NetTcpBinding(SecurityMode.Transport);

            binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
            binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;
            binding.Security.Mode = SecurityMode.Transport;
            binding.MaxReceivedMessageSize = 524288;
            binding.ReaderQuotas.MaxStringContentLength = 524288;
            binding.CloseTimeout.Add(new TimeSpan(00, 30, 00));
            binding.OpenTimeout.Add(new TimeSpan(00, 30, 00));
            binding.ReceiveTimeout.Add(new TimeSpan(00, 30, 00));
            return binding;
        }

        private AuthorizationServiceClient GetJsonAuthorizationServiceClient()
        {
            var client = new AuthorizationServiceClient(GetJsonBinding(), new EndpointAddress(ServiceUri));
            client.Endpoint.Behaviors.Add(new WebScriptEnablingBehavior());
            client.ClientCredentials.HttpDigest.ClientCredential = new NetworkCredential(this.UserName, this.Password, this.Domain);
            client.ClientCredentials.HttpDigest.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
            return client;
        }

        private WebHttpBinding GetJsonBinding()
        {
            var binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly)
            {
                AllowCookies = true,
                //  UseDefaultWebProxy = true,

            };
            //  binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;

            return binding;
        }    

        public override string[] GetAllScopes()
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.GetAllScopeNames(this.ApplicationName));
        }

        public override string[] FindUsersInScope(string scopeName, string usernameToMatch)
        {
            throw new NotImplementedException();
        }

        public override string[] GetScopesForUser(string username)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.GetAllAssignedScopesForUser(this.ApplicationName, username));
        }

        public override string[] GetScopesForUser(string username, string filterToMatch)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.GetAllAssignedScopesForUserWithFilter(this.ApplicationName, username, filterToMatch));
 
        }

        public override string[] GetUsersInScope(string scopeName)
        {
            throw new NotImplementedException();
        }

        public override bool IsUserInScope(string username, string scopeName)
        {
            throw new NotImplementedException();
        }

        public override bool ScopeExists(string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            return client.SafeExecution(i => i.ScopeExists(this.ApplicationName, scopeName));
        }

        public override void CreateScope(string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            client.SafeExecution(i => i.CreateScope(this.ApplicationName, scopeName, null));
        }
        public override void DeleteScope(string scopeName)
        {
            var client = GetAuthorizationServiceClient();
            client.SafeExecution(i => i.DeleteScope(this.ApplicationName, scopeName));
        }


  
    }


}
